Skip to content

Support static array literals and &[T;N] → &[T] coercion#123

Draft
coord-e wants to merge 4 commits into
mainfrom
claude/static-arrays-slices-y6hmtr
Draft

Support static array literals and &[T;N] → &[T] coercion#123
coord-e wants to merge 4 commits into
mainfrom
claude/static-arrays-slices-y6hmtr

Conversation

@coord-e

@coord-e coord-e commented Jun 13, 2026

Copy link
Copy Markdown
Owner

Summary

Three commits, each paired with a pass/fail test:

  1. Basic supportPlaceElem::Index in src/refine/env.rs: MIR place projections (*s)[i] now resolve to a select constraint on the underlying CHC array term. Handles both slice (Tuple → proj 0 → deref) and raw Array types. Adds tests/ui/fail/slice_2.rs to pair the existing pass test.

  2. Construction supportAggregateKind::Array arm in src/analyze/basic_block.rs: array literals [1, 2, 3] produce rty::Type::Array(Int, T) with element values pinned via store folds over a base existential. Element type for empty arrays is read directly from AggregateKind::Array(ty) rather than derived from elements. Paired tests: s[0] == 1 passes, s[0] == 99 → Unsat.

  3. Coercion supportPointerCoercion::Unsize arm in src/analyze/basic_block.rs: &[T; N] → &[T] coercions wrap the array place type in a (Array<Int,T>, N) tuple with the concrete length, enabling bounds verification. Paired tests: index 3 on a 4-element slice passes, index 4 → Unsat.

Test plan

  • cargo test — all existing tests continue to pass; 4 new tests pass
  • tests/ui/pass/array_literal_1.rss[0] == 1 verifies
  • tests/ui/fail/array_literal_1.rss[0] == 99 reports Unsat
  • tests/ui/pass/array_literal_2.rss[3] on 4-element slice is safe
  • tests/ui/fail/array_literal_2.rss[4] on 4-element slice reports Unsat
  • tests/ui/fail/slice_2.rss[2] with only len >= 2 guaranteed reports Unsat

Known limitations (out of scope)

  • [x; N] repeat syntax compiles to Rvalue::Repeat, not handled here
  • Direct static-array indexing without a coercion (arr[i] as array place, not slice) is not exercised by these tests

@coord-e coord-e force-pushed the claude/static-arrays-slices-y6hmtr branch from 4df27b4 to 7f78620 Compare June 13, 2026 06:49
claude added 4 commits June 23, 2026 17:24
Add `model::Slice<T>` and `Model` impls for `[T]`, `model::Slice<T>`,
and `[T; N]` so that the existing Model-trait normalization pipeline
handles these types without changes to the core type-builder logic:

- `[T]` normalizes to `model::Slice<T::Ty>` = `(Array<Int,T>, Int)`,
  matching the Vec model (`.0` = array, `.1` = length)
- `[T; N]` normalizes to `model::Array<Int, T::Ty>`, reusing the
  existing array model (N is statically known, written directly in specs)
- `&[T]` and `&mut [T]` flow through the existing reference handling

Add `Rvalue::Len` handling in `basic_block.rs` (slice length comes from
fat-pointer metadata in MIR, not a function call):
- For slice model (TupleType): project element 1 then deref the box
- For static array model (ArrayType): evaluate the const N from MIR

Add `UnOp::PtrMetadata` handling for `&[T]` references: extract the
length by deref → tuple_proj(1) → deref.

Add extern specs for `<[T]>::len`, `<[T]>::index`, `<[T]>::index_mut`,
and `<[T]>::is_empty`, mirroring the existing Vec specs.

Add test cases:
- `tests/ui/pass/slice_1.rs`: safe indexing with non-empty slice
- `tests/ui/fail/slice_1.rs`: out-of-bounds access (empty slice)
- `tests/ui/pass/slice_2.rs`: two-element slice, two safe indices

https://claude.ai/code/session_01BV92ggaKvAp7oNknvVmGsM
Switch [T] and [T;N] to both use model::Seq<T> (replacing model::Slice<T>
and model::Array<Int,T>). Remove the Slice<T> struct from std.rs and update
the index_mut extern spec constructor accordingly. Now both static arrays
and slices share the same (Array<Int,T>, Int) logical representation.

Add Path::Index / path_type() arm in env.rs: MIR (*s)[i] projections
resolve to a select on the Seq's inner array (field 0 → deref → select).
Simplify PlaceElem::Len to always use the Tuple (Seq) branch.

Add tests/ui/fail/slice_2.rs pairing the existing pass: s[2] on a slice
guaranteed only len >= 2 cannot be proven safe → Unsat.

https://claude.ai/code/session_01BV92ggaKvAp7oNknvVmGsM
Handle AggregateKind::Array in rvalue_type(): fold store operations over a
base existential to build a CHC array term, then wrap with the concrete
element count to produce Seq<T> = (Array<Int,T>, N). Element type for empty
arrays is read from AggregateKind::Array(ty) directly.

Paired tests: pass asserts s[0]==1 (correct), fail asserts s[0]==99 → Unsat.

https://claude.ai/code/session_01BV92ggaKvAp7oNknvVmGsM
Since both [T;N] and [T] now model as Seq<T>, the PointerCoercion::Unsize
cast is a model-level identity: just pass through the operand's place type.

Paired tests: pass accesses index 3 on a 4-element slice (safe), fail
accesses index 4 → Unsat.

https://claude.ai/code/session_01BV92ggaKvAp7oNknvVmGsM
@coord-e coord-e force-pushed the claude/static-arrays-slices-y6hmtr branch from 7f78620 to ac84456 Compare June 23, 2026 17:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants